﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using NeuralNetwork;
using NeuralNetwork.Neurons;
using NeuralNetwork.OutputTypes;
using NeuralNetwork.Layers;
using NeuralNetwork.Training;

namespace NeuralNetworkGUI
{
    public partial class frmMain : Form
    {
        private Network network = null;
        private LearningSet testSet = null;

        public void ShowNeuronAxonsInTreeView(TreeNode node, Neuron n)
        {
            string key = n.ID.ToString() + "Axons";
            TreeNode tn = node.Nodes.Add(key, "Axons", 3);           
            foreach (Axon ax in n.axons)
            {
                key = n.ID.ToString() + ax.To.ID.ToString();
                tn.Nodes.Add(key, ax.Weight.ToString("F2").Trim() + " => " + ax.To.ID.ToString().Trim(), 3);
            }
        }
        
        public void ShowNeuronInTreeView(TreeNode node, Neuron n)
        {
            string neuron_string_key = "Neuron" + n.ID.ToString().Trim();
            string neuron_string_text = "Neuron (" + n.ToString().Trim() + ")";
        
            
            TreeNode tn = node.Nodes.Add(neuron_string_key, neuron_string_text, 2);
            tn.Nodes.Add(neuron_string_key + "potential", "Potential = " + n.Potential.ToString("F3"), 4);
            tn.Nodes.Add(neuron_string_key + "value", "Value = " + n.Value.ToString("F3"), 5);
            tn.Nodes.Add(neuron_string_key + "error", "Error = " + n.Error.ToString("F3"), 6);
           
            ShowNeuronAxonsInTreeView(tn, n);
        
        }
        
        public void ShowNetworkInTreeView(TreeView view)
        {
            view.Nodes.Clear();

            TreeNode root = view.Nodes.Add("Network", "Network", 1);

            TreeNode il = root.Nodes.Add("Input", "Input Layer (" + network.input.neurons.Count.ToString() + ")", 0);            
            foreach (Neuron n in network.input.neurons)
            {
                ShowNeuronInTreeView(il, n);
            }

            int i = 1;
            TreeNode hln = null;            
            foreach (Layer hl in network.layers)
            {
                if (hl is HiddenLayer)
                {
                    hln = root.Nodes.Add("Hidden" + i.ToString().Trim(), 
                                         "Hidden Layer " + i.ToString().Trim() + "(" + hl.neurons.Count.ToString() + ")", 
                                         0);
                    foreach (Neuron n in hl.neurons)
                    {
                        ShowNeuronInTreeView(hln, n);
                    }
                    i++;
                }
            }

            TreeNode ol = root.Nodes.Add("Output", "Output Layer (" + network.output.neurons.Count.ToString() + ")", 0);            
            foreach (Neuron n in network.output.neurons)
            {
                ShowNeuronInTreeView(ol, n);
            }
        
        }
        
        public frmMain()
        {
            InitializeComponent();
        }

        private void ThreadSafeWriteToLog(string s)
        {
            if (listBox1.InvokeRequired)
            {
                Log.WriteLine f = new Log.WriteLine(WriteLineToTextBox);
                listBox1.Invoke(f, s);
            }
            else
            {
                WriteLineToTextBox(s);
            }
        }

        private void WriteLineToTextBox(string s)
        {
            string[] sl = s.Split(System.Environment.NewLine.ToCharArray());
            foreach (string i in sl)
                if (i.Trim() != "") listBox1.Items.Add(i);
            listBox1.Refresh();
            listBox1.SelectedIndex = listBox1.Items.Count - 1;
        }

        private void TrainNetworkUsingSimuletadAnnealing(object parameter)
        {
            frmSimulatedAnnealing frm = new frmSimulatedAnnealing();
            if (frm.ShowDialog() == DialogResult.OK)
            {
                LearningSet ts = (LearningSet)parameter;

                TrainingSimulatedAnnealing tsa = new TrainingSimulatedAnnealing(frm.Start, frm.Stop, frm.Cycles);
                tsa.Train(ts.network, ts.tests, 0.01);
            }
        }

        private void TrainNetworkUsingBackPropagation(object parameter)
        {
            frmBackPropagation frm = new frmBackPropagation();
            if (frm.ShowDialog() == DialogResult.OK)
            {
                LearningSet ts = (LearningSet)parameter;

                TrainingBackPropagation tsbp = new TrainingBackPropagation(frm.LearningFactor);
                tsbp.Train(ts.network, ts.tests, 0.01, frm.Cycles);
            }
        }

      
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ofdLoad.ShowDialog() == DialogResult.OK)
            {
                if (network != null)
                {
                    if (MessageBox.Show("A network is already loaded. Replacing existing network?", "Replace existing network?", MessageBoxButtons.YesNo) == DialogResult.No)
                        return;
                }

                network = new Network();
                network.log.SetWriteFunction(ThreadSafeWriteToLog);
                network.log.Level = Log.LogLevel.LOG_INFO;
                network.Load(ofdLoad.FileName);

                ShowNetworkInTreeView(trvNetworks);

                MessageBox.Show("Network loaded","",MessageBoxButtons.OK,MessageBoxIcon.Information);
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sfdSave.ShowDialog() == DialogResult.OK)
            {
                if (network != null)
                    network.Save(sfdSave.FileName);
                else
                    MessageBox.Show("No network to Save. Please build or load a network");
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {            

        }

        private void backPropagationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ((network != null) && (testSet != null))
            {
                Thread t = new Thread(TrainNetworkUsingBackPropagation);
                t.Start(testSet);

                ShowNetworkInTreeView(trvNetworks);
            }
            else
            {
                MessageBox.Show("No network to Train. Please build or load a network");
            }
        }

        private void simulatedAnnealingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ((network != null) && (testSet != null))
            {
                Thread t =  new Thread(TrainNetworkUsingSimuletadAnnealing);
                t.Start(testSet);

                ShowNetworkInTreeView(trvNetworks);
            }
            else
            {
                MessageBox.Show("No network to Train. Please build or load a network");
            }
        }
       

        private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (network != null) ShowNetworkInTreeView(trvNetworks);
        }
        
        private void imagesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (network != null)
            {
                frmImageTestSet fds = new frmImageTestSet();
                
                List<Test> tests = new List<Test>();
                if (fds.ShowDialog(tests, network.OutputValues) == DialogResult.OK)
                { 
                    testSet = new LearningSet(network, tests);

                    simulatedAnnealingToolStripMenuItem.Enabled = true;
                    backPropagationToolStripMenuItem.Enabled = true;
                }
            }
        }

        private void feedforwardNetworkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            List<Output> outputs = new List<Output>();
            frmImageRecognition fir = new frmImageRecognition();
            if (fir.ShowDialog(outputs) == DialogResult.OK)
            {
                tssMain.Text = "Constructing network .. please wait";
                tssMain.Refresh();

                network = new Network();
                network.log.SetWriteFunction(ThreadSafeWriteToLog);
                network.log.Level = Log.LogLevel.LOG_INFO;
                network.FeedForward(fir.InputNeurons,
                                    fir.HiddenLayers,
                                    fir.NeuronsInHiddenLayers,
                                    outputs,
                                    fir.ActivationFunction,
                                    fir.Normalization);

                tssMain.Text = "Network constructed";
                tssMain.Refresh();

                quickTestToolStripMenuItem.Enabled = true;
                saveToolStripMenuItem.Enabled = true;

                ShowNetworkInTreeView(trvNetworks);
            }
        }

        private void quickTestToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (network == null) return;

            if (ofdTest.ShowDialog() == DialogResult.OK)
            {                
                Bitmap t = new Bitmap(ofdTest.FileName);

                List<Output> result = network.input.Observe(t);
                string s = "";
                foreach(Output o in result) 
                {
                    if (s != "") s += ", ";
                    s += o.ToString();
                }
                s = "Image " + Path.GetFileNameWithoutExtension(ofdTest.FileName) + " is ( " + s + " )";
                MessageBox.Show(s, "Result", MessageBoxButtons.OK, MessageBoxIcon.Information);

                ShowNetworkInTreeView(trvNetworks);

                network.Reset();
            }
        }
    }
}
